#ifndef AVALON_THREADPOOL_HPP
#define AVALON_THREADPOOL_HPP
#include <deque>
#include <vector>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/type_traits.hpp>
#include "Avalon/Base/Functional.hpp"
#include "Avalon/Base/Out.hpp"
#include "Avalon/Threading/Async.hpp"

namespace Avalon {
namespace Threading {

  /*! \class ThreadPool
   *  \brief Implements a thread pool for running Tasks.
   */
  class ThreadPool : private boost::noncopyable {
    public:

      //! Specifies the return type of a function.
      template<typename F>
      struct ReturnType {
        typedef typename boost::function_traits<
          typename boost::remove_pointer<F>::type>::result_type Type;
      };

      //! Constructs a ThreadPool.
      /*!
        \param maxThreadCount The maximum number of threads to allocate.
      */
      ThreadPool(int maxThreadCount);

      ~ThreadPool();

      //! Returns the maximum thread count.
      int GetMaxThreadCount() const;

      //! Queues a function to be run within an allocated thread.
      /*!
        \param function The function execute.
        \param result The result of the <i>function</i>.
      */
      template<typename F, typename R>
      void Queue(const F& function, Out<Async<R> > result) {
        Task<F, R>* task = new Task<F, R>(*this, function, Store(result));
        QueueTask(task);
      }

      //! Queues a function to be run within an allocated thread.
      /*!
        \param function The function execute.
        \param result The result of the <i>function</i>.
      */
      template<typename F>
      void Queue(const F& function,
          Out<Async<typename ReturnType<F>::Type> > result) {
        Queue<F, typename ReturnType<F>::Type>(function, Store(result));
      }

    private:
      class TaskThread;
      class BaseTask : private boost::noncopyable {
        public:
          BaseTask();

          virtual ~BaseTask();

          virtual void Run() = 0;
      };
      template<typename F, typename R>
      class Task : public BaseTask {
        public:
          Task(ThreadPool& threadPool, const F& function, Out<Async<R> > result)
              : m_threadPool(threadPool),
                m_function(function),
                m_result(result.Get()) {}

          virtual ~Task() {}

          void Run() {
            Invoker<R>()(m_function, m_result);
          }

        private:
          ThreadPool& m_threadPool;
          F m_function;
          Async<R>* m_result;

          template<typename R>
          struct Invoker {
            void operator ()(const F& function, Async<R>* result) const {
              try {
                result->SetResult(function());
              } catch(std::exception& e) {
                result->SetException(e);
              }
            }
          };

          template<>
          struct Invoker<void> {
            void operator ()(const F& function, Async<void>* result) const {
              try {
                function();
                result->SetResult();
              } catch(std::exception& e) {
                result->SetException(e);
              }
            }
          };

          static void InvokeFunction(const F& function, Async<R>* result) {
            Invoker<R>()(function, result);
          }
      };
      boost::mutex m_mutex;
      int m_maxThreadCount;
      int m_runningThreads;
      std::deque<TaskThread*> m_threads;
      std::deque<BaseTask*> m_tasks;
      boost::condition_variable m_threadsFinishedCondition;

      void QueueTask(BaseTask* task);
      bool AddThread(TaskThread* taskThread);
      void RemoveThread(TaskThread* taskThread);
  };
}
}

#endif // AVALON_THREADPOOL_HPP
